home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / b / b.lha / B / src / bed / getc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-11-24  |  16.1 KB  |  767 lines

  1. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  2.  
  3. /* $Header: getc.c,v 2.5 85/08/22 16:02:44 timo Exp $ */
  4.  
  5. /* B editor -- read key definitions from file */
  6.  
  7. #include "b.h"
  8. #include "feat.h"
  9. #ifdef LINDA
  10. #include "b1mem.h"
  11. #define syserr EDsyserr
  12. #else !LINDA
  13. #define freemem(p) free(p)
  14. #endif !LINDA
  15. #include "file.h"
  16. #include "keys.h"
  17.  
  18. #include <ctype.h>
  19.  
  20. extern bool dflag;
  21.  
  22. #define ESC '\033'
  23.  
  24. /*
  25. This file contains a little parser for key definition files.
  26. To allow sufficient freedom in preparing such a file, a simple
  27. grammar has been defined according to which the file is parsed.
  28. The parsing process is extremely simple, as it can be done
  29. top-down using recursive descent.
  30.  
  31.  
  32. Lexical conventions:
  33.  
  34. - Blanks between lexical symbols are gnored.
  35. - From '#' to end of line is comment (except inside strings).
  36. - Strings are delimited by single or double quotes and
  37.   use the same escape sequences as C strings, plus:
  38.   \e or \E means an ESCape ('\033').
  39. - Command names are like C identifiers ([a-zA-Z_][a-zA-Z0-9_]*).
  40.   Upper/lower case distinction is significant.
  41. - numbers are octal or decimal integers in C-style (leading zero means octal)
  42. - After '^' a character is expected, this must be a letter or one of @^_[]\ .
  43.  
  44. Syntax in modified BNF ([] mean 0 or 1, * means 0 or more, + means 1 or more):
  45.  
  46.    file: line*
  47.    line: [def] [comment]
  48.    def: commandname '=' rhs
  49.    rhs: item+
  50.    item: string | '^' character | number
  51.  
  52.  
  53. Notes:
  54.  
  55. - A definition for command "term_init" defines a string to be sent
  56.   TO the terminal at initialization time, e.g. to set programmable
  57.   function key definitions.  Similar for "term_done" on exiting.
  58. - Command names are  conventional editor commands.
  59.  
  60. */
  61.  
  62.  
  63. #ifndef LINDA
  64. /* Defines subroutine that used to be in the support levels: */
  65.  
  66. Hidden string getmem(nbytes)
  67.     unsigned nbytes;
  68. {
  69.     string malloc();
  70.     string pointer= malloc(nbytes);
  71.  
  72.     if (pointer == NULL)
  73.         syserr("memory full in initkeys");
  74.     return pointer;
  75. }
  76.  
  77. Hidden string regetmem(pp, nbytes)
  78.     string *pp;
  79.     unsigned nbytes;
  80. {
  81.     *pp= realloc(*pp, nbytes);
  82.     if (*pp == NULL)
  83.         syserr("memory full in initkeys (regetmem)");
  84. }
  85. #endif !LINDA
  86.  
  87.  
  88. #define COMMENT '#' /* Not B-like but very UNIX-like */
  89. #define MAXDEFS 100
  90.  
  91. Hidden FILE *fp; /* File from which to read */
  92. Hidden string filename; /* File name for error messages */
  93. Hidden char nextc; /* Next character to be analyzed */
  94. Hidden bool eof; /* EOF seen? */
  95. Hidden int lcount; /* Current line number */
  96. Hidden bool errcount; /* Number of errors detected */
  97.  
  98.  
  99. struct tabent {
  100.     int code;
  101.     string name;
  102.     string def;
  103. };
  104.  
  105. /* Table of key definitions, mostly filled by reading definitions from a file.
  106.    The "I" macro has two arguments: the default for termcap and that for
  107.    the IBM PC.  It expands to either depending on whether IBMPC is defined.
  108.    'def' fields initialized with a string starting with '=' are termcap names,
  109.    and are replaced by the corresponding termcap entry (NULL if none).
  110.    On the IBM PC, 'extended codes' are by convention a null character
  111.    followed by another character (usually the scan code).  Since the null
  112.    character is rather unsuitable for use in C strings, we use \377 (hex FF)
  113.    instead, a code which has no assigned graphic is the extended IBM PC
  114.    character set.  E.g., F1 is 0-59, which we encode as \377\073 (since
  115.    \073 is octal for 59 decimal).  For the exact codes, see for instance the
  116.    BASIC 2.0 manual, appendix G, or the XT Technical Reference, page 2-14.
  117. */
  118.  
  119. #ifdef IBMPC
  120. #define I(tc, ibm) ibm
  121. #else !IBMPC
  122. #define I(tc, ibm) tc
  123. #endif !IBMPC
  124.  
  125. Visible struct tabent deftab[MAXDEFS] = {
  126.     /* General rule:
  127.        unix => ctrl-x
  128.        IBM  => alt-x
  129.        where x is first letter of command name
  130.     */
  131.     {0377, "ignore", NULL}, /* Entry to ignore a key */
  132.     {COPY, "copy", I(NULL, "\377\056")},
  133.     {DELETE, "delete", I(NULL, "\377\040")},
  134.     {DELETE, "delete", I(NULL, "\377\123")}, /* IBM DEL key */
  135.     {ACCEPT, "accept", I(NULL, "\377\022")}, /* ^E, alt-E */
  136.     {ACCEPT, "end", I(NULL, "\377\117")}, /* IBM END key */
  137.     {'\t', "tab", NULL}, /* = ACCEPT in Bed, insert tab in Linda */
  138.     {UNDO, "undo"}, /* Always backspace = ^H */
  139.     {REDRAW, "redraw", I(NULL, "\377\046")}, /* ^L, alt-L */
  140.     {REDRAW, "look"},
  141.     {RETURN, "newline"}, /* Always ^M */
  142.     {REDO, "redo", I(NULL, "\177")}, /* IBM ctrl-BS = ASCII 177 (DEL) */
  143.     {EXIT, "exit", I(NULL, "\377\055")}, /* ^X, alt-X */
  144.  
  145. #ifdef RECORDING
  146.     /*
  147.      * The IBM-PC has a problem here in ANSI.SYS mode: ctrl-P is
  148.      * unusable because it means Print Screen, and alt-R is unusable
  149.      * because it transmits 0, 19 but 19 is ctrl-S which means stop
  150.      * output :-(.
  151.      * The only reasonable place to put the things would then be on
  152.      * function keys.  You should do this in the key definitions file. (?)
  153.      */
  154.     {PLAYBACK, "play", I(NULL, "\377\031")},
  155.     {PLAYBACK, "playback", I(NULL, "\377\031")},
  156.     {RECORD, "record", I(NULL, "\377\023")},
  157. #endif RECORDING
  158.  
  159. #ifdef LINDA
  160.     {BFIND, "bfind", I(NULL, "\377\060")},
  161.     {FIND, "find", I(NULL, "\377\041")},
  162.     {GLOBAL, "global", I(NULL, "\377\042")},
  163.     {JOIN, "join", I(NULL, "\377\044")},
  164.     {TOGGLE, "toggle", I(NULL, "\377\024")},
  165.     {YANK, "yank", I(NULL, "\377\025")},
  166.     {LITERAL, "literal", I(NULL, "\377\057")}, /* ^V, alt-V */
  167. #endif LINDA
  168.  
  169.     {WIDEN, "widen", I("=k1", "\377\073")}, /* IBM F1 */
  170.     {NARROW, "narrow", I("=k2", "\377\075")}, /* IBM F3 (!!!) */
  171.     {NARROW, "first"},
  172.     {RNARROW, "rnarrow", I("=k3", "\377\076")}, /* IBM F4 (!!!) */
  173.     {RNARROW, "last"},
  174.     {EXTEND, "extend", I("=k4", "\377\074")}, /* IBM F2 (!!!) */
  175.     {UPARROW, "up", I("=ku", "\377\110")},
  176.     {UPLINE, "upline", I("=k5", "\377\110")},
  177.     {LEFTARROW, "left", I("=kl", "\377\113")},
  178.     {PREVIOUS, "previous", I("=k6", NULL)},
  179.     {RITEARROW, "right", I("=kr", "\377\115")},
  180.     {NEXT, "next", I("=k7", NULL)},
  181.     {DOWNARROW, "down", I("=kd", "\377\120")},
  182.     {DOWNLINE, "downline", I("=k8", "\377\120")},
  183.  
  184.     {GOTO, "goto", I("\033g", NULL)}, /* Doesn't exist on IBM */
  185. #ifdef HELPFUL
  186.     {HELP, "help", I("\033?", "\377\104")}, /* ESC ?, IBM F10 */
  187. #endif HELPFUL
  188.  
  189.     {0, "term_init", I("=ks", NULL)},
  190.     {0, "term_done", I("=ke", NULL)},
  191. };
  192.  
  193. #undef I
  194.  
  195. Hidden int ndefs;
  196.  
  197.  
  198. Hidden Procedure err(fmt, arg)
  199.     string fmt, arg;
  200. {
  201.     if (errcount == 0)
  202.         fprintf(stderr, "Errors in key definitions file:\n");
  203.     ++errcount;
  204.     fprintf(stderr, "%s, line %d: ", filename, lcount);
  205.     fprintf(stderr, fmt, arg);
  206.     fprintf(stderr, "\n");
  207. }
  208.  
  209. Hidden Procedure adv()
  210. {
  211.     int c;
  212.  
  213.     if (eof)
  214.         return;
  215.     c= getc(fp);
  216.     if (c == EOF) {
  217.         nextc= '\n';
  218.         eof= Yes;
  219.     }
  220.     else {
  221.         nextc= c;
  222.         if (c == '\n')
  223.             ++lcount;
  224.     }
  225. }
  226.  
  227. Hidden Procedure skipsp()
  228. {
  229.     while (nextc == ' ' || nextc == '\t')
  230.         adv();
  231. }
  232.  
  233. Hidden int lookup(name)
  234.     string name;
  235. {
  236.     int i;
  237.  
  238.     for (i= 0; i < ndefs; ++i) {
  239.         if (deftab[i].name != NULL && strcmp(name, deftab[i].name) == 0)
  240.             return i;
  241.     }
  242.     return -1;
  243. }
  244.  
  245. Hidden Procedure store(code, name, def)
  246.     int code;
  247.     string name;
  248.     string def;
  249. {
  250.     struct tabent *d, *last= deftab+ndefs; 
  251.     string p, q;
  252.  
  253.     /* Undefine conflicting definitions.  Conflicts arise
  254.        when a command definition is an initial subsequence
  255.        of another, or vice versa.  Key definitions (code < 0)
  256.        are not undefined. */
  257.     if (code > 0) {
  258.         for (d= deftab; d < last; ++d) {
  259.             if (d->code >= 0 && d->def != NULL) {
  260.                 for (p= def, q= d->def; *p == *q; ++p, ++q) {
  261.                     if (*p == '\0' || *q == '\0') {
  262.                         d->def= NULL;
  263.                         break;
  264.                     }
  265.                 }
  266.             }
  267.         }
  268.     }
  269.  
  270.     /* Find a free slot with the same code and NULL definition */
  271.     /* (For code == 0, the name must match instead of the code,
  272.        and the definition need not be NULL) */
  273.     for (d= deftab; d < last; ++d) {
  274.         if (code == 0 ? strcmp(name, d->name) == 0
  275.             : (d->code == code && d->def == NULL))
  276.             break;
  277.     }
  278.     if (d == last) { /* Extend definition table */
  279.         if (ndefs >= MAXDEFS) {
  280.             err("Too many key definitions", "");
  281.             return;
  282.         }
  283.         ++ndefs;
  284.         d->code= code;
  285.         d->name= name;
  286.     }
  287.     d->def= def;
  288. }
  289.  
  290. Hidden string savestr(s)
  291.     string s;
  292. {
  293.     string new;
  294.  
  295.     new= getmem((unsigned) (strlen(s) + 1));
  296.     strcpy(new, s);
  297.     return new;
  298. }
  299.  
  300. Hidden Procedure append(to, item)
  301.     string *to, item;
  302. {
  303.     int len= strlen(*to) + strlen(item) + 1;
  304.     regetmem(to, len);
  305.     strcat(*to, item);
  306. }
  307.  
  308. Hidden string getname()
  309. {
  310.     char buffer[20];
  311.     string bp;
  312.  
  313.     if (!isalpha(nextc) && nextc != '_') {
  314.         err("No name where expected", "");
  315.         return NULL;
  316.     }
  317.     for (bp= buffer; isalnum(nextc) || nextc == '_'; ) {
  318.         if (bp < buffer + sizeof buffer - 1)
  319.             *bp++ = nextc;
  320.         adv();
  321.     }
  322.     *bp= '\0';
  323.     return savestr(buffer);
  324. }
  325.  
  326. Hidden int getnumber()
  327. {
  328.     int base= (nextc == '0') ? 8 : 10;
  329.     int i= 0;
  330.     int d;
  331.  
  332.     for (;; adv()) {
  333.         d= nextc-'0';
  334.         if (d < 0 || d > 9)
  335.             break;
  336.         if (d > base) {
  337.             err("8 or 9 in octal number", "");
  338.             return 0;
  339.         }
  340.         i= i*base + d;
  341.     }
  342.     return i;
  343. }
  344.  
  345. Hidden string getstring()
  346. {
  347.     char buf[256]; /* Arbitrary limit */
  348.     char quote= nextc;
  349.     char c;
  350.     int len= 0;
  351.  
  352.     adv();
  353.     while (nextc != quote) {
  354.         if (nextc == '\n') {
  355.             err("closing string quote not found", "");
  356.             return NULL;
  357.         }
  358.         if (nextc != '\\') {
  359.             c= nextc;
  360.             adv();
  361.         }
  362.         else {
  363.             adv();
  364.             switch (nextc) {
  365.  
  366.             case 'r': c= '\r'; adv(); break;
  367.             case 'n': c= '\n'; adv(); break;
  368.             case 'b': c= '\b'; adv(); break;
  369.             case 't': c= '\t'; adv(); break;
  370.             case 'f': c= '\f'; adv(); break;
  371.  
  372.             case 'E':
  373.             case 'e': c= ESC; adv(); break;
  374.  
  375.             case '0': case '1': case '2': case '3':
  376.             case '4': case '5': case '6': case '7':
  377.                 c= nextc-'0';
  378.                 adv();
  379.                 if (nextc >= '0' && nextc < '8') {
  380.                     c= 8*c + nextc-'0';
  381.                     adv();
  382.                     if (nextc >= '0' && nextc < '8') {
  383.                         c= 8*c + nextc-'0';
  384.                         adv();
  385.                     }
  386.                 }
  387.                 break;
  388.  
  389.             default: c=nextc; adv(); break;
  390.  
  391.             }
  392.         }
  393.         if (len >= sizeof buf) {
  394.             err("string too long", "");
  395.             return NULL;
  396.         }
  397.         buf[len++]= c;
  398.     }
  399.     adv();
  400.     buf[len]= '\0';
  401.     return savestr(buf);
  402. }
  403.  
  404. Hidden string getitem()
  405. {
  406.     char buf[2];
  407.     string keyname;
  408.     int i;
  409.  
  410.     switch (nextc) {
  411.     case '"':
  412.     case '\'':
  413.         return getstring();
  414.     case '^':
  415.         adv();
  416.         if (isalpha(nextc) || index("@^_[]\\?", nextc)) {
  417.             if (nextc == '?')
  418.                 buf[0]= '\177';
  419.             else
  420.                 buf[0]= nextc & 037;
  421.             buf[1]= '\0';
  422.             adv();
  423.             return savestr(buf);
  424.         }
  425.         err("Invalid character after '^'", "");
  426.         return NULL;
  427.     default:
  428.         if (isdigit(nextc)) {
  429.             buf[0]= getnumber();
  430.             buf[1]= '\0';
  431.             return savestr(buf);
  432.         }
  433.         if (isalpha(nextc) || nextc == '_') {
  434.             keyname= getname(); /* Cannot fail */
  435.             if (strlen(keyname) == 1)
  436.                 return savestr(keyname);
  437.                 /* Single letters stand for themselves */
  438.             i= lookup(keyname);
  439.             if (i < 0 || deftab[i].code <= 0) {
  440.                 err("%s: not a key name", keyname);
  441.                 freemem(keyname);
  442.                 return NULL;
  443.             }
  444.             else if (deftab[i].def == NULL) {
  445.                 err("%s: undefined key", keyname);
  446.                 freemem(keyname);
  447.                 return NULL;
  448.             }
  449.             else
  450.                 return savestr(deftab[i].def);
  451.         }
  452.         err("Invalid item", "");
  453.         return NULL;
  454.     }
  455. }
  456.  
  457. Hidden string getrhs()
  458. {
  459.     string first, item;
  460.  
  461.     skipsp();
  462.     first= getitem();
  463.     if (first != NULL) {
  464.         for (;;) {
  465.             skipsp();
  466.             if (nextc == '\n' || nextc == COMMENT)
  467.                 break;
  468.             item= getitem();
  469.             if (item == NULL) {
  470.                 freemem(first);
  471.                 return NULL;
  472.             }
  473.             append(&first, item);
  474.             freemem(item);
  475.         }
  476.     }
  477.     return first;
  478. }
  479.  
  480. Hidden Procedure getdef()
  481. {
  482.     string name;
  483.     int key;
  484.     string rhs;
  485.  
  486.     name= getname();
  487.     if (name == NULL)
  488.         return;
  489.     skipsp();
  490.     if (nextc != '=') {
  491.         err("Command name %s not followed by '='", name);
  492.         return;
  493.     }
  494.     key= lookup(name);
  495.     if (key < 0) {
  496.         err("Unknown command: %s", name);
  497.         return;
  498.     }
  499.     if (deftab[key].code < 0) {
  500.         err("No redefinition of %s allowed", name);
  501.         return;
  502.     }
  503.     adv();
  504.     rhs= getrhs();
  505.     if (rhs != NULL)
  506.         store(deftab[key].code, name, rhs);
  507. }
  508.  
  509. Hidden Procedure getline()
  510. {
  511.     adv();
  512.     skipsp();
  513.     if (nextc != COMMENT && nextc != '\n')
  514.         getdef();
  515.     while (nextc != '\n')
  516.         adv();
  517. }
  518.  
  519. #ifndef NDEBUG
  520. Hidden Procedure dump(where)
  521.     string where;
  522. {
  523.     int i;
  524.     string s;
  525.  
  526.     printf("\nDump of key definitions %s.\n\n", where);
  527.     printf("Code    Name            Definition\n");
  528.     for (i= 0; i < ndefs; ++i) {
  529.         printf("%04o    ", deftab[i].code);
  530.         if (deftab[i].name != NULL)
  531.             printf("%-15s ", deftab[i].name);
  532.         else
  533.             printf("%16s", "");
  534.         s= deftab[i].def;
  535.         if (s != NULL) {
  536.             for (; *s != '\0'; ++s) {
  537.                 if (isascii(*s) && (isprint(*s) || *s == ' '))
  538.                     fputc(*s, stdout);
  539.                 else
  540.                     printf("\\%03o", *s&0377);
  541.             }
  542.         }
  543.         printf("\n");
  544.     }
  545.     fflush(stdout);
  546. }
  547. #endif !NDEBUG
  548.  
  549. Hidden Procedure countdefs()
  550. {
  551.     struct tabent *d;
  552.  
  553.     d= deftab;
  554.     while (d->name != NULL || d->code != 0 || d->def != NULL) {
  555.         ++d;
  556.         if (d >= deftab+MAXDEFS)
  557.             syserr("too many predefined keys");
  558.     }
  559.     ndefs= d-deftab;
  560. }
  561.  
  562. Hidden Procedure process()
  563. {
  564.     errcount= 0;
  565.     lcount= 1;
  566.     eof= No;
  567.     do {
  568.         getline();
  569.     } while (!eof);
  570. }
  571.  
  572. Hidden bool try(dir, file, type)
  573.     string dir, file, type;
  574. {
  575.     char buffer[200];
  576.  
  577. #ifdef IBMPC
  578.     sprintf(buffer, "%.150s\\%.9s%.3s", dir, file, type);
  579. #else !IBMPC
  580.     sprintf(buffer, "%.150s/%.20s%.20s", dir, file, type);
  581. #endif !IBMPC
  582.     fp= fopen(buffer, "r");
  583.     if (fp == NULL)
  584.         return No;
  585.     filename= buffer;
  586.     process();
  587.     fclose(fp);
  588. #ifndef NDEBUG
  589.     if (dflag)
  590.         dump("after try");
  591. #endif NDEBUG
  592.     return Yes;
  593. }
  594.  
  595. #ifndef IBMPC
  596. Hidden Procedure readtermcap()
  597. {
  598.     string tgetstr();
  599.     char buffer[1024]; /* Constant dictated by termcap manual entry */
  600.     static char area[1024];
  601.     string endarea= area;
  602.     string anentry;
  603.     struct tabent *d, *last;
  604.  
  605.     switch (tgetent(buffer, getenv("TERM"))) {
  606.  
  607.     default:
  608.         fprintf(stderr, "*** Bad tgetent() return value.\n");
  609.         /* Fall through */
  610.     case -1:
  611.         fprintf(stderr, "*** Can't read termcap.\n");
  612.         /* Fall through again */
  613.     case 0:
  614.         fprintf(stderr, "*** No description for your terminal.\n");
  615.         exit(1);
  616.  
  617.     case 1:
  618.         break;
  619.     }
  620.     last= deftab+ndefs;
  621.     for (d= deftab; d < last; ++d) {
  622.         if (d->def != NULL && d->def[0] == '=') {
  623.             anentry= tgetstr(d->def+1, &endarea);
  624.             if (anentry != NULL && anentry[0] != '\0')
  625.                 d->def= anentry;
  626.             else
  627.                 d->def= NULL;
  628.         }
  629.     }
  630. }
  631. #endif !IBMPC
  632.  
  633. Visible Procedure initkeys()
  634. {
  635.     string term= NULL;
  636.  
  637.     countdefs();
  638. #ifndef NDEBUG
  639.     if (dflag)
  640.         dump("before termcap");
  641. #endif NDEBUG
  642. #ifndef IBMPC
  643.     readtermcap();
  644. #ifndef NDEBUG
  645.     if (dflag)
  646.         dump("after termcap");
  647. #endif NDEBUG
  648.     term= getenv("TERM");
  649.     if (term != NULL && term[0] == '\0')
  650.         term= NULL;
  651. #endif !IBMPC
  652. #ifdef DEBUG
  653.     /* Try in the current directory. Only for debugging porpoises. */
  654.     if (term != NULL)
  655.         if (try(".", keyfile, term)) return;
  656. #endif DEBUG
  657.     if (term != NULL) {
  658.         if (try(homedir, keyfile, term)) return;
  659.         if (try(libdir, keyfile, term)) return;
  660.     }
  661. #ifdef DEBUG
  662.     if (try(".", keyfile, deftype)) return;
  663. #endif DEBUG
  664.     if (try(homedir, keyfile, deftype)) return;
  665.     if (try(libdir, keyfile, deftype)) return;
  666. #ifndef NDEBUG
  667.     printf("[No key definitions file found, using defaults.]\n");
  668. #endif !NDEBUG
  669. }
  670.  
  671.  
  672. /* Output a named string to the terminal */
  673.  
  674. Hidden Procedure outstring(name)
  675.     string name;
  676. {
  677.     int i= lookup(name);
  678.     string def;
  679.  
  680.     if (i >= 0 && (def= deftab[i].def) != NULL)
  681.         fputs(def, stdout);
  682. }
  683.  
  684.  
  685. /* Output the terminal's initialization sequence, if any. */
  686.  
  687. Visible Procedure
  688. initgetc()
  689. {
  690.     outstring("term_init");
  691. }
  692.  
  693.  
  694. /* Output a sequence, if any, to return the terminal to a 'normal' state. */
  695.  
  696. Visible Procedure endgetc()
  697. {
  698.     outstring("term_done");
  699. }
  700.  
  701.  
  702. /* Read a command from the keyboard, decoding composite key definitions. */
  703.  
  704. #ifndef IBMPC
  705. /* Strip high bit from input characters (matters only on PWB systems?) */
  706. #define getch() (getchar() & 0177)
  707. #endif !IBMPC
  708.  
  709. Visible int inchar()
  710. {
  711.     int c;
  712.     struct tabent *d, *last;
  713.     char buffer[100];
  714.     int len;
  715.  
  716.     c= getch();
  717.     if (c == EOF)
  718.         return c;
  719. #ifdef IBMPC
  720.     if (c == 0)
  721.         c= 0377;
  722. #endif IBMPC
  723.     last= deftab+ndefs;
  724.     for (d= deftab; d < last; ++d) {
  725.         if (d->code > 0 && d->def != NULL && c == (d->def[0] & 0377))
  726.             break;
  727.     }
  728.     if (d == last) {
  729.         if (c == ESC) {
  730.             /* Kludge to make ESC-char by default equal to
  731.                char|MASK -- the command definitions do the rest:
  732.                e.g. WIDEN is 'w'|MASK, so ESC-w means WIDEN. */
  733.             c= getch();
  734.             if (c == EOF)
  735.                 return EOF;
  736.             return (c&0177) | MASK;
  737.         }
  738.         return c;
  739.     }
  740.     if (d->def[1] == '\0')
  741.         return d->code;
  742.     buffer[0]= c;
  743.     len= 1;
  744.     for (;;) {
  745.         c= getch();
  746.         if (c == EOF)
  747.             return EOF;
  748.         buffer[len]= c;
  749.         if (len < sizeof buffer - 1)
  750.             ++len;
  751.         for (d= deftab; d < last; ++d) {
  752.             if (d->code > 0 && d->def != NULL
  753.                 && strncmp(buffer, d->def, len) == 0)
  754.                 break;
  755.         }
  756.         if (d == last) {
  757.             if (buffer[0] == ESC && len == 2) {
  758.                 /* Same kludge as above */
  759.                 return c&0177 | MASK;
  760.             }
  761.             return 0377; /* Hope this rings a bell */
  762.         }
  763.         if (d->def[len] == '\0')
  764.             return d->code;
  765.     }
  766. }
  767.